package com.jhh.pay.weishenma.security;

import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.binary.Base64;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;

import net.sf.json.JSONArray;
import net.sf.json.JSONObject;

@Component
@PropertySource(value = "classpath:/security.properties", ignoreResourceNotFound = true)
public class Base {

	private static final String DEFAULT_CHARSET = "UTF-8";
	private static final String HEX_CHARS = "0123456789abcdef";
	public static final String sendOrderUrl = "https://lt-aquarius.wsmtec.com/OLM/index.php"; // 发送订单url
	public static final String checkOrderUrl = "https://lt-orderquery.wsmtec.com/index.php"; // 查询订单url

	@Value("${wsm.clientkeystore}")
	private String clientkeystore; // 证书保存路径
	private String clientkeystorePassword = "abc+123"; // 证书使用密码
	@Value("${wsm.mid}")
	private int mid; // 商户号
	@Value("${wsm.cpm}")
	private int cpm; // 商户商品编号
	@Value("${wsm.shmyc}")
	private String shmyc; // 商户密钥串
	@Value("${wsm.csjmsy}")
	private String csjmsy; // 加密解密串
	private final Map<String, String> config = new LinkedHashMap<>(); // 数据库连接信息
	private final List<String> encryptKeys = new ArrayList<>(); // 订单内需要加密的字段
	private final List<String> intKeys = new ArrayList<>(); // 订单内需要转整型的字段

	public Base() {
		// 数据库连接信息
		config.put("db_name", "jhhpay");
		config.put("name", "JHH-DEV");
		config.put("pwd", "abc+123V");

		// 订单内需要加密的字段
		encryptKeys.add("xm");
		encryptKeys.add("sfzh");
		encryptKeys.add("sjh");
		encryptKeys.add("gsmc");
		encryptKeys.add("gsdh");
		encryptKeys.add("lxrxm1");
		encryptKeys.add("lxrdh1");
		encryptKeys.add("lxrxm2");
		encryptKeys.add("lxrdh2");
		encryptKeys.add("kh");
		encryptKeys.add("ylsjh");

		// 订单内需要转整型的字段
		intKeys.add("sqje");
		intKeys.add("qx");
		intKeys.add("ts");
		intKeys.add("zfje");
		intKeys.add("ktje");
	}

	// 设置订单内的值（加密/转型/替换）
	public void setOrderVals(Map<String, Object> rowData, String orderKey, Object orderVal) {
		String setOrderVal = "";
		// 测试数据，简单拼接
		if (orderVal == null || orderVal.equals("")) {
			rowData.put(orderKey, setOrderVal);
		} else {
			setOrderVal = orderVal.toString(); // obj转string
			if (encryptKeys.contains(orderKey)) { // 查看key是否存在需要加密的字段列表中
				setOrderVal = Base.encrypt(setOrderVal, csjmsy, csjmsy);
			}

			if (intKeys.contains(orderKey)) { // 查看key是否存在需要转整型字段的列表中
				rowData.put(orderKey, Integer.parseInt(setOrderVal)); // String 转 Integer
			} else {
				rowData.put(orderKey, setOrderVal);
			}
		}
	}

	public void setOrderVals(JSONObject jo, String key, Object orderVal) {
		String setOrderVal = "";
		// 测试数据，简单拼接
		if (orderVal == null || orderVal.equals("")) {
			jo.put(key, setOrderVal);
		} else {
			setOrderVal = orderVal.toString(); // obj转string
			if (encryptKeys.contains(key)) { // 查看key是否存在需要加密的字段列表中
				setOrderVal = Base.encrypt(setOrderVal, csjmsy, csjmsy);
			}
			if (intKeys.contains(key)) { // 查看key是否存在需要转整型字段的列表中
				jo.put(key, Integer.parseInt(setOrderVal)); // String 转 Integer
			} else {
				jo.put(key, setOrderVal);
			}
		}
	}

	// 填充订单内通用字段
//	public Map fillOrder(Map rowData) throws Exception {
//		rowData.put("shddh", CreateShddhByUUId());
//		rowData.put("mid", mid);
//		rowData.put("cpm", cpm);
//		rowData.put("shmyc", shmyc);
//		rowData.put("timestamp", getSecondTimestamp());
//		rowData.put("fkxx", GetFkxx()); // 拼接风控信息（fkxx）字段，安文档填写。没有信息，键值照常填写，值为""
//		rowData.put("zfmx", GetZfmx()); // 拼接支付明细（zfmx）字段,字段内value的总和等于sqje
//		rowData.put("sign", generateSendSign(rowData)); // 创建签名
//		return rowData;
//	}
	
	// 填充订单内通用字段
	public void fillOrder(JSONObject order, Map<String, Object> map) throws Exception {
//		rowData.put("shddh", CreateShddhByUUId());
		map.put("mid", mid);
		map.put("cpm", cpm);
		map.put("shmyc", shmyc);
		map.put("timestamp", getSecondTimestamp());
		map.put("fkxx", GetFkxx()); // 拼接风控信息（fkxx）字段，安文档填写。没有信息，键值照常填写，值为""
		map.put("zfmx", GetZfmx(order)); // 拼接支付明细（zfmx）字段,字段内value的总和等于sqje
		map.put("sign", generateSendSign(map)); // 创建签名
	}

	public String toHexString(byte[] b) {
		StringBuffer sb = new StringBuffer();
		for (int i = 0; i < b.length; i++) {
			sb.append(HEX_CHARS.charAt(b[i] >>> 4 & 0x0F));
			sb.append(HEX_CHARS.charAt(b[i] & 0x0F));
		}
		return sb.toString();
	}

	// md5加密
	public String getMD5(String message) {
		String md5str = "";
		try {
			// 1 创建一个提供信息摘要算法的对象，初始化为md5算法对象
			MessageDigest md = MessageDigest.getInstance("MD5");

			// 2 将消息变成byte数组
			byte[] input = message.getBytes();

			// 3 计算后获得字节数组,这就是那128位了
			byte[] buff = md.digest(input);

			// 4 把数组每一字节（一个字节占八位）换成16进制连成md5字符串
			md5str = bytesToHex(buff);

		} catch (Exception e) {
			e.printStackTrace();
		}
		return md5str;
	}

	/**
	 * 二进制转十六进制
	 * 
	 * @param bytes
	 * @return
	 */
	public String bytesToHex(byte[] bytes) {
		StringBuffer md5str = new StringBuffer();
		// 把数组每一字节换成16进制连成md5字符串
		int digital;
		for (int i = 0; i < bytes.length; i++) {
			digital = bytes[i];

			if (digital < 0) {
				digital += 256;
			}
			if (digital < 16) {
				md5str.append("0");
			}
			md5str.append(Integer.toHexString(digital));
		}
		return md5str.toString().toUpperCase();
	}

	// sha1加密
	public String SHA1Utilencrypt(String content) {
		try {
			MessageDigest md = MessageDigest.getInstance("SHA-1");
			md.update(content.getBytes());
			byte result[] = md.digest();
			return toHexString(result);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	// 创建签名
	public String generateSendSign(Map<String, Object> param) throws NoSuchAlgorithmException, UnsupportedEncodingException {
		// Sign= Md5(Sha1(mid+shmyc+shddh+xm+sfzh+sjh+kh+sqje)+timestamp)
		String sha1 = SHA1Utilencrypt(new StringBuilder().append(param.get("mid")).append(param.get("shmyc"))
				.append(param.get("shddh")).append(param.get("xm")).append(param.get("sfzh")).append(param.get("sjh"))
				.append(param.get("kh")).append(param.get("sqje")).toString());
		String orderSign = getMD5(sha1 + param.get("timestamp"));
		return orderSign.toLowerCase(); // 英文大写转小写
	}
	
	// 创建签名
	public String generateSendSign(JSONObject jo) throws NoSuchAlgorithmException, UnsupportedEncodingException {
		// Sign= Md5(Sha1(mid+shmyc+shddh+xm+sfzh+sjh+kh+sqje)+timestamp)
		System.out.println("before sign : " + new StringBuilder().append(jo.get("mid")).append(jo.get("shmyc"))
				.append(jo.get("shddh")).append(jo.get("xm")).append(jo.get("sfzh")).append(jo.get("sjh"))
				.append(jo.get("kh")).append(jo.get("sqje")));
		String sha1 = SHA1Utilencrypt(new StringBuilder().append(jo.get("mid")).append(jo.get("shmyc"))
				.append(jo.get("shddh")).append(jo.get("xm")).append(jo.get("sfzh")).append(jo.get("sjh"))
				.append(jo.get("kh")).append(jo.get("sqje")).toString());
		String orderSign = getMD5(sha1 + jo.get("timestamp"));
		return orderSign.toLowerCase(); // 英文大写转小写
	}

	/**
	 * 获取精确到秒的时间戳
	 * 
	 * @return
	 */
	public static int getSecondTimestamp() {
		Date date = new Date();
		String timestamp = String.valueOf(date.getTime());
		int length = timestamp.length();
		if (length > 3) {
			return Integer.valueOf(timestamp.substring(0, length - 3));
		} else {
			return 0;
		}
	}

	// 创建商户订单号,测试使用
	public static String CreateShddhByUUId() {
		int machineId = 1;// 最大支持1-9个集群机器部署
		int hashCodeV = UUID.randomUUID().toString().hashCode();
		if (hashCodeV < 0) {// 有可能是负数
			hashCodeV = -hashCodeV;
		}
		// 0 代表前面补充0
		// 4 代表长度为4
		// d 代表参数为正数型
		return machineId + String.format("%015d", hashCodeV);
	}

	// 订单内字段加密
	public static String encrypt(String data, String key, String iv) {
		try {
			Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
			int blockSize = cipher.getBlockSize();

			byte[] dataBytes = data.getBytes(DEFAULT_CHARSET);
			int plaintextLength = dataBytes.length;
			if (plaintextLength % blockSize != 0) {
				plaintextLength = plaintextLength + (blockSize - (plaintextLength % blockSize));
			}

			byte[] plaintext = new byte[plaintextLength];
			System.arraycopy(dataBytes, 0, plaintext, 0, dataBytes.length);

			SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(), "AES");
			IvParameterSpec ivspec = new IvParameterSpec(iv.getBytes());

			cipher.init(Cipher.ENCRYPT_MODE, keyspec, ivspec);
			byte[] encrypted = cipher.doFinal(plaintext);
			Base64 base64 = new Base64();
			String encryptedVal = base64.encodeToString(encrypted);
//			String encryptedVal = new sun.misc.BASE64Encoder().encode(encrypted);
			return encryptedVal;

		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

	// 订单内字段解密
	public static String desEncrypt(String data, String key, String iv) throws Exception {
		try {
//			byte[] encrypted1 = new sun.misc.BASE64Decoder().decodeBuffer(data);
			byte[] encrypted1 = Base64.decodeBase64(data);
			Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
			SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(), "AES");
			IvParameterSpec ivspec = new IvParameterSpec(iv.getBytes());

			cipher.init(Cipher.DECRYPT_MODE, keyspec, ivspec);

			byte[] original = cipher.doFinal(encrypted1);
			String originalString = new String(original);
			originalString = originalString.trim();
			return originalString;
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

	// 风控信息字段，先转json，在转成字符串
	public static String GetFkxx() {
		Map<String, String> map = new LinkedHashMap<>();
		map.put("地址类别","2");
		map.put("设备MAC","");
		map.put("坐标经纬度","");
		map.put("社交账号	","719960893");
		map.put("社交账号类型","1");
		map.put("月化利率	","0.8%");
		map.put("地址信息是否校验","0");
		map.put("身份证多项信息是否验证","1");
		map.put("身份证多项信息验证渠道","face++");
		map.put("工作信息是否验证","0");
		map.put("工作信息验证渠道","");
		map.put("学历/学籍信息是否验证","0");
		map.put("学历/学籍信息验证渠道","");
		map.put("联系人信息是否验证","0");
		map.put("联系人信息验证渠道","");
		map.put("通讯录是否读取","1");
		map.put("是否获取电信运营商数据	","1");
		map.put("是否获取电商数据","0");
		map.put("是否获取社保数据","0");
		map.put("是否获取公积金数据","1");
		map.put("身份证号、姓名、银行卡号一致性验证","1");
		map.put("身份证号、姓名、银行卡号一致性验证渠道","银生宝");
		map.put("网贷黑名单是否命中","2");
		map.put("网贷黑名单验证渠道","");
		map.put("法院黑名单是否命中","2");
		map.put("法院黑名单验证渠道","");
		map.put("公安信息黑名单是否命中","2");
		map.put("公安信息黑名单验证渠道	","");
		map.put("第三方支付交易编号","");
		JSONObject jsonObj = JSONObject.fromObject(map);
		return jsonObj.toString();
	}

	// 支付明细字段，先转json，在转成字符串
//	public String GetZfmx() throws Exception {
//		List list = new ArrayList();
//		Map rowData2 = new LinkedHashMap();
//		Map rowData1 = new LinkedHashMap();
//		rowData2.put("flag", "2");
//		rowData2.put("khhbh", "1005");
//		rowData2.put("xm", encrypt("测试", csjmsy, csjmsy));
//		rowData2.put("sfzh", encrypt("210211199809214379", csjmsy, csjmsy));
//		rowData2.put("yhkh", encrypt("6222620410005461772", csjmsy, csjmsy));
//		rowData2.put("value", "59999");
//		rowData2.put("jjfwf", "2");
//		rowData2.put("ybdk", "2");
//
//		rowData1.put("flag", "1");
//		rowData1.put("khhbh", "1001");
//		rowData1.put("xm", Base.encrypt("测试金融", csjmsy, csjmsy));
//		rowData1.put("zhmc", "中国银行星海支行");
//		rowData1.put("yhkh", Base.encrypt("6222620410005461772", csjmsy, csjmsy));
//		rowData1.put("sheng", "上海");
//		rowData1.put("shi", "上海");
//		rowData1.put("value", "1");
//		rowData1.put("jjfwf", "1");
//		rowData1.put("ybdk", "1");
//
//		list.add(rowData1);
//		list.add(rowData2);
//		JSONArray json = JSONArray.fromObject(list);
//		return json.toString();
//	}
	
	// 支付明细字段，先转json，在转成字符串
		public String GetZfmx(JSONObject jo) throws Exception {
			List<Map<String, String>> list = new ArrayList<>();
			Map<String, String> rowData2 = new LinkedHashMap<>();
//			Map rowData1 = new LinkedHashMap();
			rowData2.put("flag", "2");
			rowData2.put("khhbh", jo.getString("khh"));
			rowData2.put("xm", encrypt(jo.get("xm").toString(), csjmsy, csjmsy));
			rowData2.put("sfzh", encrypt(jo.get("sfzh").toString(), csjmsy, csjmsy));
			rowData2.put("yhkh", encrypt(jo.getString("kh"), csjmsy, csjmsy));
			rowData2.put("value", jo.getString("sqje"));
			rowData2.put("jjfwf", "2");
			rowData2.put("ybdk", "2");

//			rowData1.put("flag", "1");
//			rowData1.put("khhbh", "1001");
//			rowData1.put("xm", Base.encrypt("测试金融", csjmsy, csjmsy));
//			rowData1.put("zhmc", "中国银行星海支行");
//			rowData1.put("yhkh", Base.encrypt("6222620410005461772", csjmsy, csjmsy));
//			rowData1.put("sheng", "上海");
//			rowData1.put("shi", "上海");
//			rowData1.put("value", "1");
//			rowData1.put("jjfwf", "1");
//			rowData1.put("ybdk", "1");

//			list.add(rowData1);
			list.add(rowData2);
			JSONArray json = JSONArray.fromObject(list);
			return json.toString();
		}

	public String getClientkeystore() {
		return clientkeystore;
	}

	public void setClientkeystore(String clientkeystore) {
		this.clientkeystore = clientkeystore;
	}

	public String getClientkeystorePassword() {
		return clientkeystorePassword;
	}

	public void setClientkeystorePassword(String clientkeystorePassword) {
		this.clientkeystorePassword = clientkeystorePassword;
	}

	public int getMid() {
		return mid;
	}

	public void setMid(int mid) {
		this.mid = mid;
	}

	public int getCpm() {
		return cpm;
	}

	public void setCpm(int cpm) {
		this.cpm = cpm;
	}

	public String getShmyc() {
		return shmyc;
	}

	public void setShmyc(String shmyc) {
		this.shmyc = shmyc;
	}

	public String getCsjmsy() {
		return csjmsy;
	}

	public void setCsjmsy(String csjmsy) {
		this.csjmsy = csjmsy;
	}

	@Override
	public String toString() {
		return "Base [clientkeystore=" + clientkeystore + ", clientkeystorePassword=" + clientkeystorePassword
				+ ", mid=" + mid + ", cpm=" + cpm + ", shmyc=" + shmyc + ", csjmsy=" + csjmsy + ", config=" + config
				+ ", encryptKeys=" + encryptKeys + ", intKeys=" + intKeys + "]";
	}

}
